package org.cn.zhaozhx.util.download;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

/**
 * @ClassName: org.cn.zhaozhx.download1.BatchDownloadFile
 * @Description: 分批量下载文件
 */
public class BatchDownloadFile implements Runnable {

    private DownloadInfo downloadInfo; // 下载文件信息
    private long[] startPos; // 一组开始下载位置
    private long[] endPos;// 一组结束下载位置

    private static final int SLEEP_SECONDS = 500; // 休眠时间
    private DownloadFile[] fileItem;// 子线程下载
    // 文件长度
    private int length;
    // 是否第一个文件
    private boolean first = true;
    // 是否停止下载
    private boolean stop = false; // 临时文件信息
    private File tempFile;

    public BatchDownloadFile(DownloadInfo downloadInfo) {
        this.downloadInfo = downloadInfo;
        String tempPath = this.downloadInfo.getFilePath() + File.separator + downloadInfo.getFileName() + ".position";
        tempFile = new File(tempPath); // 如果存在读入点位置的文件
        if (tempFile.exists()) {
            first = false;
            // 就直接读取内容
            try {
                readPosInfo();
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else {
            // 数组的长度就要分成多少段的数量
            startPos = new long[downloadInfo.getSplitter()];
            endPos = new long[downloadInfo.getSplitter()];
        }
    }

    public void run() {
        // 首次下载,获取下载文件长度
        if (first) {
            length = this.getFileSize();// 获取文件长度
            if (length == -1) {
                LogUtils.log("file length is know!");
                stop = true;
            } else if (length == -2) {
                LogUtils.log("read file length is error!");
                stop = true;
            } else if (length > 0) {
                /**
                 * eg start: 1, 3, 5, 7, 9 end: 3, 5, 7, 9, length
                 */
                for (int i = 0, len = startPos.length; i < len; i++) {
                    int size = i * (length / len);
                    startPos[i] = size;
                    // 设置最后一个结束点的位置
                    if (i == len - 1) {
                        endPos[i] = length;
                    } else {
                        size = (i + 1) * (length / len);
                        endPos[i] = size;
                    }
                    LogUtils.log("start-end Position[" + i + "]: " + startPos[i] + "-" + endPos[i]);
                }
            } else {
                LogUtils.log("get file length is error, download is stop!");
                stop = true;
            }
        }
        // 子线程开始下载
        if (!stop) {
            // 创建单线程下载对象数组
            fileItem = new DownloadFile[startPos.length];// startPos.length =
                                                         // downloadInfo.getSplitter()
            for (int i = 0; i < startPos.length; i++) {
                try {
                    // 创建指定个数单线程下载对象,每个线程独立完成指定块内容的下载
                    fileItem[i] =
                        new DownloadFile(downloadInfo.getUrl(), this.downloadInfo.getFilePath() + File.separator
                            + downloadInfo.getFileName(), startPos[i], endPos[i], i);
                    fileItem[i].start();// 启动线程,开始下载
                    LogUtils.log("Thread: " + i + ", startPos: " + startPos[i] + ", endPos: " + endPos[i]);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            // 循环写入下载文件长度信息
            while (!stop) {
                try {
                    writePosInfo();
                    LogUtils.log("downloading......");
                    Thread.sleep(SLEEP_SECONDS);
                    stop = true;
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                for (int i = 0; i < startPos.length; i++) {
                    if (!fileItem[i].isDownloadOver()) {
                        stop = false;
                        break;
                    }
                }
            }
            LogUtils.info("Download task is finished!");
        }
    }

    /**
     * 将写入点数据保存在临时文件中 * @author hoojo
     * @createDate 2011-9-23 下午05:25:37 * 
     * @throws IOException
     */
    private void writePosInfo() throws IOException {
        DataOutputStream dos = new DataOutputStream(new FileOutputStream(tempFile));
        dos.writeInt(startPos.length);
        for (int i = 0; i < startPos.length; i++) {
            dos.writeLong(fileItem[i].getStartPos());
            dos.writeLong(fileItem[i].getEndPos());
            // LogUtils.info("[" + fileItem[i].getStartPos() + "#" +
            // fileItem[i].getEndPos() + "]");
        }
        dos.close();
    }

    /**
     * <b>function:</b>读取写入点的位置信息 * 
     * @author hoojo
     * @createDate 2011-9-23 下午05:30:29
     * @throws IOException
     */
    private void readPosInfo() throws IOException {
        DataInputStream dis = new DataInputStream(new FileInputStream(tempFile));
        int startPosLength = dis.readInt();
        startPos = new long[startPosLength];
        endPos = new long[startPosLength];
        for (int i = 0; i < startPosLength; i++) {
            startPos[i] = dis.readLong();
            endPos[i] = dis.readLong();
        }
        dis.close();
    }

    /**
     * <b>function:</b> 获取下载文件的长度 * 
     * @author hoojo
     * @createDate 2011-9-26 下午12:15:08 * 
     * @return
     */
    private int getFileSize() {
        int fileLength = -1;
        try {
            URL url = new URL(this.downloadInfo.getUrl());
            HttpURLConnection conn = (HttpURLConnection)url.openConnection();
            DownloadFile.setHeader(conn);
            int stateCode = conn.getResponseCode();
            // 判断http status是否为HTTP/1.1 206 Partial Content或者200 OK
            if (stateCode != HttpURLConnection.HTTP_OK && stateCode != HttpURLConnection.HTTP_PARTIAL) {
                LogUtils.log("Error Code: " + stateCode);
                return -2;
            } else if (stateCode >= 400) {
                LogUtils.log("Error Code: " + stateCode);
                return -2;
            } else {
                // 获取长度
                fileLength = conn.getContentLength();
                LogUtils.log("FileLength: " + fileLength);
            }
            // 读取文件长度
            /*
             * for (int i = 1; ; i++) { String header =
             * conn.getHeaderFieldKey(i); if (header != null) { if
             * ("Content-Length".equals(header)) { fileLength =
             * Integer.parseInt(conn.getHeaderField(i)); break; } } else {
             * break;
             * 
             * } }
             */
            DownloadFile.printHeader(conn);
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return fileLength;
    }
}
